﻿using System;
using OverStore.Configuration.Validation;
using OverStore.Exceptions.Configuration;
using OverStore.Runtime.OverStoreContext;
using OverStore.Runtime.ObjectRepository.ChangesTracker;

namespace OverStore.Configuration.ObjectRepository.ChangesTracker
{
    /// <summary>
    /// Allows to create instance of <see cref="DelegateChangesTracker{TInstance, TIdentifier, TState}"/> 
    /// class that uses delegate for define instance state.
    /// </summary>
    /// <typeparam name="TInstance">The type of the instance.</typeparam>
    /// <typeparam name="TIdentifier">The type of the identifier.</typeparam>
    /// <typeparam name="TState">The type of the state uses for define .</typeparam>
    public sealed class DelegateChangesTrackerConfiguration<TInstance, TIdentifier, TState> : IComponentConfiguration<IChangesTracker<TInstance>>,
        ISupportValidation
        where TInstance : class
        where TState : IEquatable<TState>
    {
        /// <summary>
        /// Gets or sets the delegate that obtains instance state.
        /// </summary>
        public Func<TInstance, TState> GetStateFunc { get; set; }

        /// <summary>
        /// Creates the new instance of <see cref="IChangesTracker{TInstance}"/> type.
        /// </summary>
        /// <param name="context">The current context provides set of services may be used by changes tracker.</param>
        /// <returns>
        /// The new <see cref="IChangesTracker{TInstance}"/> instance.
        /// </returns>
        public IChangesTracker<TInstance> CreateComponent(IOverStoreContext context)
        {
            if (context == null)
                throw new ArgumentNullException("context");
            if (GetStateFunc == null)
                throw this.ConfigurationException("GetStateFunc function is null");
            return new DelegateChangesTracker<TInstance, TIdentifier, TState>(context.PersistenceServiceProvider, GetStateFunc);
        }

        /// <summary>
        /// Perform component validation. Method should validate only 
        /// component configuration itself, not sub components.
        /// If configuration is valid, null result should returns.
        /// </summary>
        public ValidationResult Validate()
        {
            return new ValidationResult(this)
                .AddError(GetStateFunc == null, "Get dump delegate is missing.");
        }
    }
}
